package com.leyou.trade.service.impl;
import	java.util.HashMap;

import ch.qos.logback.core.util.FileUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import com.leyou.auth.constants.JwtConstants;
import com.leyou.auth.utils.UserContext;
import com.leyou.commons.dto.PageDTO;
import com.leyou.commons.exception.LyException;
import com.leyou.commons.utils.CookieUtils;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.trade.constants.PayConstants;
import com.leyou.trade.dto.*;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.comment;
import com.leyou.trade.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderCommentService;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.MD5CodeUtil;
import com.leyou.trade.utils.PayHelper;
import com.leyou.trade.utils.TokenContext;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.leyou.commons.constants.MQConstants.ExchangeConstants.*;
import static com.leyou.commons.constants.MQConstants.RoutingKeyConstants.*;
import static com.leyou.commons.constants.MQConstants.QueueConstants.*;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
    @Autowired
    private ItemClient itemClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private OrderDetailService detailService;
    @Autowired
    private OrderLogisticsService logisticsService;
    @Autowired
    private PayHelper payHelper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private OrderCommentService commentService;
    @Override
    public Long createOrder(OrderDTO orderDTO, HttpServletRequest request) {
        //把token存入context里
        TokenContext.setToken(CookieUtils.getCookieValue(request, JwtConstants.COOKIE_NAME));
        //订单保存，查三张表
        //key:skuId,value:num数量
        Map<Long, Integer> cartsMap = orderDTO.getCarts();
        //提交的要购买的商品数量
        int size = cartsMap.size();
        //order
        //根据skuIds查询sku的集合
        List<SkuDTO> skuDTOS = this.itemClient.querySkuByIds(new ArrayList<>(cartsMap.keySet()));
        //提交的数量和查到的数量不匹配则表示下单出了系统故障
        if(size!=skuDTOS.size()){
        throw new LyException(500,"下单系统出现异常,请重新提交");
        }
        //总价格
        long totalFee = 0;

        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (SkuDTO skuDTO : skuDTOS) {
            Long skuId = skuDTO.getId();
            Integer num = cartsMap.get(skuId);
           totalFee+= skuDTO.getPrice()*num;
        }
        Order order = new Order();
        order.setTotalFee(totalFee);
        order.setActualFee(totalFee);
        order.setPaymentType(orderDTO.getPaymentType());
        order.setPostFee(0L);
        order.setUserId(UserContext.getUser().getId());
        order.setStatus(1);
        boolean success = save(order);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        //orderDetail
        //订单详情封装
        // 2.1.定义一个OrderDetail的集合
        List<OrderDetail> details = new ArrayList<>();
        // 2.2.遍历sku集合，转为detail
        for (SkuDTO sku : skuDTOS) {
            // 2.2.1.商品数量
            int num = cartsMap.get(sku.getId());
            // 2.2.2.组装OrderDetail
            OrderDetail detail = new OrderDetail();
            detail.setOrderId(order.getOrderId());
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            detail.setNum(num);
            detail.setSkuId(sku.getId());
            detail.setSpec(sku.getSpecialSpec());
            detail.setPrice(sku.getPrice());
            detail.setTitle(sku.getTitle());
            // 2.2.3.装入detail集合
            details.add(detail);
        }
        // 2.3.批量新增
        success = detailService.saveBatch(details);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 3.写orderLogistics

        // 3.1.查询收货地址
        AddressDTO address = userClient.queryAddressById(orderDTO.getAddressId());
        // 3.2.填写物流信息
        OrderLogistics logistics = address.toEntity(OrderLogistics.class);
        logistics.setOrderId(order.getOrderId());
        // 3.3.写入数据库
        success = logisticsService.save(logistics);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        //4.减库存
        try {
            itemClient.deductStock(cartsMap);
        } catch (FeignException e) {
            throw new LyException(e.status(),e.getMessage());
        }
        amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, order.getOrderId());
        //返回订单id
        return order.getOrderId();
    }

    /**
     * 获取支付地址url
     * @param orderId
     * @return
     */
    @Override
    public String getPayUrl(Long orderId) {
        String payUrl = "";
        String s = redisTemplate.opsForValue().get(orderId.toString());
        if(s!=null){
          payUrl = s;
          return payUrl;
      }
        Order order = getById(orderId);
        //判断订单是否存在
        if(order==null){
            throw new LyException(400,"订单不存在");
        }
        //判断订单是否已经付过款
        if(order.getStatus()!= OrderStatus.INIT.getValue()){
            throw new LyException(400,"订单已经付款");
        }
        //获取订单总金额
        Long actualFee = order.getActualFee();
        payUrl = payHelper.getPayUrl(orderId, 1L/*actualFee*/, PayConstants.ORDER_DESC);
        //把payUrl存入redis缓存中
        redisTemplate.opsForValue().set(orderId.toString(),payUrl,20L, TimeUnit.MINUTES);
        return payUrl;
    }

    /** todo
     * 处理微信的异步通知
     * @param data 通知内容
     * @return 处理结果
     */
    @Override
    public void handleNotify(Map<String, String> data) {
        // 1.业务标示校验
        payHelper.checkResultCode(data);
        // 2.签名校验
        payHelper.checkResponseSignature(data);

        // 3.订单状态校验（保证幂等，防止重复通知）
        String outTradeNo = data.get(PayConstants.ORDER_NO_KEY);
        String totalFee = data.get(PayConstants.TOTAL_FEE_KEY);
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
            // 数据有误
            throw new RuntimeException("响应数据有误，订单金额或编号为空！");
        }
        Long orderId = Long.valueOf(outTradeNo);
        Order order = getById(orderId);
        if (!order.getStatus().equals(OrderStatus.INIT.getValue())) {
            // 说明订单已经支付过了，属于重复通知，直接返回
            return;
        }
        // 4.订单金额校验
        Long total = Long.valueOf(totalFee);
        if (!total.equals(1L/*order.getActualFee()*/)) {
            throw new RuntimeException("订单金额有误，我要报警了！");
        }

        // 5.修改订单状态，更新状态和支付时间两个字段
        update().set("status", OrderStatus.PAY_UP.getValue()).set("pay_time", new Date())
                // 条件包括订单id和订单状态必须为1，乐观锁保证幂等
                .eq("order_id", orderId).eq("status", OrderStatus.INIT.getValue()).update();
        log.info("处理微信支付通知成功！{}", data);
    }

    /** todo
     * 前端轮询发送请求查询订单支付状态
     * @param orderId
     * @return
     */
    @Override
    public Integer queryOrderState(Long orderId) {
        // 查询订单
        Order order = getById(orderId);
        // 判断是否存在
        if (order == null) {
            throw new LyException(400, "订单不存在！");
        }
        return order.getStatus();
    }

    /**
     * 监听订单状态，对订单进行清理，并添加库存
     * @param orderId
     */
    @Override
    public void evictOrderIfNecessary(Long orderId) {
        // 1.查询订单
        Order order = getById(orderId);
        if(order == null){
            // 订单不存在，无需处理
            return;
        }
        // 2.判断订单是否支付
        if(order.getStatus() != OrderStatus.INIT.getValue()){
            // 订单已处理，无需重复处理
            return;
        }
        // 3.如果未支付，需要关闭订单
        boolean boo = update().set("status", OrderStatus.CLOSED.getValue())
                .set("close_time", new Date())
                .eq("order_id", orderId)
                // 通过乐观锁进一步保证幂等效果
                .eq("status", OrderStatus.INIT.getValue())
                // 执行update
                .update();
        if(!boo){
            // 更新失败，订单状态已经改变，无需处理
            return;
        }
        log.info("已关闭超时未支付订单：{}", orderId);
        // 4.查询OrderDetail
        List<OrderDetail> details = detailService.query().eq("order_id", orderId).list();

        // 5.获取商品及商品数量信息
        Map<Long, Integer> skuMap = new HashMap<>();
        // 得到其中的商品和数量信息
        for (OrderDetail detail : details) {
            skuMap.put(detail.getSkuId(), detail.getNum());
        }
        // 6.恢复库存
        itemClient.addStock(skuMap);
    }

    /**
     * 提醒商户发货，短信通知
     * @param orderId
     */
    @Override
    public void RemindDelivery(Long orderId) {
        //判断此订单是否有效
        Order order = getById(orderId);
        if(order==null){
            throw new LyException(400,"此订单不存在");
        }
        String orderID = String.valueOf(orderId);
        log.info("提示信息==>订单:"+orderID+"  请尽快发货");
    }

    /**
     * 确认收货
     * @param orderId
     */
    @Override
    public void confirmreceipt(Long orderId) {
        Order order = getById(orderId);
        if(order==null){
            throw new LyException(400,"订单信息有误");
        }
        order.setStatus(OrderStatus.CONFIRMED.getValue());
        updateById(order);
        log.info("订单"+orderId+" 已被确认收货");
}

    /** todo
     * 订单评价
     * @param map
     * @return
     */
    @Override
    public Boolean comment(comment comment) {
      comment.setImageUrl("https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100");
        try {
            commentService.save(comment);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 查看评价
     * @param id
     * @return
     */
    @Override
    public Map<String, Object> checkComment(Long id) {
        comment comment = commentService.getById(id);
        List<OrderDetail> details = detailService.query().eq("order_id", id).list();
        Map<String,Object> map = new HashMap<>();
        map.put("comment",comment);
        map.put("datails",details);
        return map;
    }


    /**
     * 分页查询
     * @param searchParam
     * @return
     */
    @Override
    public PageDTO<OrderDetailsDTO> queryOrderList(SearchParamDTO searchParam) {

        String key = searchParam.getKey();
        int page = searchParam.getPage();
        int rows = searchParam.getRows();
        Integer status = searchParam.getStatus();
        Long userId = UserContext.getUser().getId();
        List<Order> orderList = this.query()
                .eq(status != 0, "status", status)
                .eq(userId != null, "user_id", userId).list();
        //查询订单细节
        List<OrderDetailsDTO> orderDetailsDTOSList = new ArrayList<OrderDetailsDTO>();

        for (Order order : orderList) {

            List<OrderDetail> orderDetailList = this.detailService.query().like(key != null, "title", key).eq("order_id", order.getOrderId()).list();

            if (orderDetailList.size()>0){
                List<DetailsDTO> detailsDTOS = DetailsDTO.convertEntityList(orderDetailList);
                OrderDetailsDTO orderDetailsDTO = new OrderDetailsDTO();
                BeanUtils.copyProperties(order, orderDetailsDTO);
                orderDetailsDTO.setOrderDetails(detailsDTOS);
                orderDetailsDTO.setCreateTime(order.getCreateTime().getTime());
                orderDetailsDTO.setEndTime(order.getEndTime().getTime());
                orderDetailsDTOSList.add(orderDetailsDTO);
            }

        }


        Long total = Integer.valueOf(orderDetailsDTOSList.size()).longValue();
        Long totalPage = (total % rows == 0) ? (total / rows) : (total / rows + 1);
        int index = (page - 1) * rows;
        List<OrderDetailsDTO> result = new ArrayList<>();
        for (int i = 0; i < orderDetailsDTOSList.size(); i++) {
            if (i >= index && i < index + rows) {
                result.add(orderDetailsDTOSList.get(i));
            }
        }
        System.out.println(result.toString());
        return new PageDTO<>(total, totalPage, result);

    }

    @Override
    public void  backPay(Long orderId,Long price) {
        if (orderId==null){
            throw new LyException(400,"参数部位空！");
        }
        Order order = this.getById(orderId);
        if (order==null){
            throw new LyException(400,"订单不存在！");
        }
        if (order.getActualFee().intValue()!=price.intValue()){
            throw new LyException(400,"订单金额不正确！");
        }

        try {
            //构建参数
            Map<String, String> dataMap = new HashMap<>();
            dataMap.put("appid", "wx8397f8696b538317");
            dataMap.put("mch_id", "1473426802");
            //自行实现该随机串
            dataMap.put("nonce_str", MD5CodeUtil.encode("12345", "MD5"));
            dataMap.put("out_trade_no", "" + orderId);
            dataMap.put("out_refund_no", "1217752501201407033233368018");
            dataMap.put("total_fee", "1");
            dataMap.put("refund_fee", "1");
            dataMap.put("refund_desc", "退款");
            //生成签名
            String sign = WXPayUtil.generateSignature(dataMap, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb");
            dataMap.put("sign", sign);
            //map数据转xml
            String xmlString = WXPayUtil.mapToXml(dataMap);
            //发起退款
            String s = doRefund("1473426802", "https://api.mch.weixin.qq.com/secapi/pay/refund", xmlString);
            //回调
            backurl(s);
            //修改订单状态
            Map<Long, Integer> map = new HashMap<>();

            List<OrderDetail> orderDetails = this.detailService.query().eq("order_id", orderId).list();
            boolean b =true;
            for (OrderDetail orderDetail : orderDetails) {
                //更新订单状态
                Order order1 = new Order();
                order1.setOrderId(orderDetail.getOrderId());
                order1.setStatus(5);
                b = this.updateById(order1);
                //准备库存数据
                if (map.containsKey(orderDetail.getSkuId())){
                    map.put(orderDetail.getSkuId(),map.get(orderDetail.getSkuId())+orderDetail.getNum());
                }else {
                    map.put(orderDetail.getSkuId(),orderDetail.getNum());
                }
            }
            //增加库存
            if (b){
                this.itemClient.addStock(map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public OrderDetailsDTO1 queryOrderDetail(Long orderId) {

        if (orderId==null){
            throw new LyException(400,"参数部位空！");
        }
        Order order = this.getById(orderId);
        if (order==null){
            throw new LyException(400,"订单不存在！");
        }
        OrderDetailsDTO1 orderDetailsDTO1 = new OrderDetailsDTO1();
        //订单细节
        List<OrderDetail> detailList = this.detailService.query().eq("order_id", orderId).list();
        if (detailList.size() ==0){
            throw new LyException(400,"无效订单！");
        }
        List<DetailsDTO> detailsDTOS = DetailsDTO.convertEntityList(detailList);
        //订单属性
        BeanUtils.copyProperties(order, orderDetailsDTO1);
        orderDetailsDTO1.setOrderDetails(detailsDTOS);
        orderDetailsDTO1.setConsignTime(order.getConsignTime().getTime());
        orderDetailsDTO1.setCreateTime(order.getCreateTime().getTime());
        orderDetailsDTO1.setPayTime(order.getPayTime().getTime());
        return orderDetailsDTO1;
    }

    /**
     * @param mchId 商户ID
     * @param url   请求URL
     * @param data  退款参数
     * @return
     * @throws Exception
     */
    public  String doRefund(String mchId, String url, String data) throws Exception {
        Map<String, String> toMap = WXPayUtil.xmlToMap(data);
        toMap.put("return_code","SUCCESS");
        toMap.put("return_msg","OK");
        toMap.put("result_code","SUCCESS");
        toMap.put("transaction_id","1008450740201411110005820873");
        toMap.put("refund_id","1008450740201411110005820873");
        toMap.remove("refund_desc");
        return WXPayUtil.mapToXml(toMap);
    }

    /**
     * @param data 回调数据
     * @return
     * @throws Exception
     */
    public  void  backurl(String data) throws Exception {
        Map<String, String> toMap = WXPayUtil.xmlToMap(data);
        String return_code = toMap.get("return_code");
        String total_fee = toMap.get("total_fee");
        if (!return_code.equals("SUCCESS")){
            throw new LyException(400,"支付失败！");
        }
        if (Integer.parseInt(total_fee)!=1){
            throw new LyException(400,"退款金额不正确！");
        }
    }
}